package com.netx.cubigraf.backups;
import java.io.IOException;
import java.util.TimerTask;
import com.netx.generics.basic.Logger;
import com.netx.generics.basic.Context;
import com.netx.generics.io.Directory;
import com.netx.generics.io.FileSystem;
import com.netx.generics.sql.Row;
import com.netx.generics.tasks.TaskManager;
import com.netx.generics.tasks.TaskWorker;
import com.netx.data.Connection;
import com.netx.data.Database;
import com.netx.data.DatabaseException;
import com.netx.ebs.User;
import com.netx.cubigraf.entities.Entities;
import com.netx.cubigraf.entities.Referencia;
import com.netx.cubigraf.entities.Tasks;
import com.netx.cubigraf.entities.Perfis;
import com.netx.cubigraf.shared.FsUtils;


public class DaemonTask extends TimerTask {
	
	private final Database _database;
	private final TaskManager _taskManager;
	private final FileSystem _server;

	public DaemonTask(Database database, TaskManager manager, FileSystem server) {
		_database = database;
		_taskManager = manager;
		_server = server;
	}

	public void run() {
		Connection c = null;
		final Logger logger = Context.getRootLogger();
		logger.info("starting a new DaemonTask");
		try {
			while(_taskManager.getNumRunningTasks() < _taskManager.getMaxConcurrentTasks()) {
				c = _database.getConnection();
				Tasks tasks = Entities.getTasks(c);
				Row task = tasks.getNext('c');
				if(task == null) {
					// no tasks to do, try to execute failed tasks:
					logger.info("restarting tasks");
					tasks.restartTasks('c');
					// get out of here, and let the next DaemonTask run,
					// so this doesn't run over and over again:
					return;
				}
				else {
					// get the src folder:
					long taskId = task.getLong(1);
					long servicoId = task.getLong(2);
					logger.info("starting copy task "+taskId+" for entity "+servicoId);
					Referencia ref = Entities.getServicos(c).verReferencia(servicoId);
					User desenhador = Entities.getServicos(c).verDesenhador(servicoId);
					Perfis p = Entities.getPerfis(c);
					long userID = desenhador.getPrimaryKey();
					Row r = p.getPerfil(userID);
					FileSystem client = FsUtils.getClientFS(r.getString(1), r.getChar(2), r.getString(3), r.getString(4));
					Directory src = client.getDirectory(ref.toString());
					if(src == null) {
						// cancel the task:
						logger.info("[reference]: \""+ref.toString()+"\" was not found for user "+desenhador.getName());
						tasks.removeTask(taskId);
					}
					else {
						// get the dest folder:
						String destPath = FsUtils.getServerPathFor(ref);
						Directory dest = _server.getDirectory(destPath);
						if(dest != null) {
							logger.info("deleting directory \""+dest.getPath()+"\"");
							dest.delete(true);
						}
						logger.info("creating directory \""+destPath+"\"");
						dest = _server.mkdirs(destPath);
						// prepare end and failure workers:
						TaskWorker end = new EndWorker(taskId, src, dest, _database);
						TaskWorker failed = new FailedWorker(taskId, _database);
						// start task:
						_taskManager.startTask(new DirectoryCopyTask(desenhador.getName(), src, dest, end, failed, logger));
					}
				}
				c.close();
			}
		}
		catch(Throwable t) {
			// database exceptions:
			logger.error(t);
			// try to close the connection, if still open:
			try {
				if(c != null) {
					c.close();
					logger.info("connection closed");
				}
			}
			catch(DatabaseException de) {
				logger.error(de);
			}
		}
	}
	
	private class EndWorker extends TaskWorker {
		private final long _taskId;
		private final Directory _src;
		private final Directory _dest;
		private final Database _database;
		
		public EndWorker(long taskId, Directory src, Directory dest, Database database) {
			_taskId = taskId;
			_src = src;
			_dest = dest;
			_database = database;
		}
		
		protected void performWork() throws DatabaseException {
			Connection c = _database.getConnection();
			Tasks tasks = Entities.getTasks(c);
			try {
				// check the folder for contents:
				if(_src.equals(_dest)) {
					// 1: move the original folder to the OLD folder:
					if(_src.getFileSystem().getDirectory("/OLD") == null) {
						_src.getFileSystem().mkdirs("OLD");
					}
					_src.renameTo("/OLD/"+_src.getName());

					// now: delete the src folder:
					//_src.delete(true);
					// remove the task:
					tasks.removeTask(_taskId);
					getTask().getLogger().info("successfully finished copying "+_src.getName());
				}
				else {
					tasks.markAsFailed(_taskId);
					getTask().getLogger().info("in folder \""+_dest.getPath()+"\": contents do not match source dir's contents");
				}
			}
			catch(IOException io) {
				// if an error occurs while moving the folder:
				tasks.markAsFailed(_taskId);
				getTask().getLogger().error(io);
			}
			c.close();
			markFinished();
		}
	}

	private class FailedWorker extends TaskWorker {
		private final long _taskId;
		private final Database _database;
		
		public FailedWorker(long taskId, Database database) {
			_taskId = taskId;
			_database = database;
		}
		
		protected void performWork() throws DatabaseException {
			Connection c = _database.getConnection();
			Tasks tasks = Entities.getTasks(c);
			tasks.markAsFailed(_taskId);
			c.close();
			markFinished();
		}
	}
}
